/**
* Tero Paananen
* tepaanan@gmail.com
* FINLAND
*/

#include "cameraitem.h"
#include <QDebug>

const int MAX_FRAME = 1;

MyVideoSurface::MyVideoSurface(QDeclarativeItem* widget, CameraUpdateIF* target, QObject* parent)
    : QAbstractVideoSurface(parent)
{
    m_targetWidget = widget;
    m_target = target;
    m_imageFormat = QImage::Format_Invalid;
    m_handleFrameCount = MAX_FRAME;
    m_differentFrame = false;
    m_hideViewFinder = false;
    m_ledLights = false;
}

MyVideoSurface::~MyVideoSurface()
{
}

bool MyVideoSurface::start(const QVideoSurfaceFormat &format)
{
    m_videoFormat = format;
    const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
    const QSize size = format.frameSize();

    if (imageFormat != QImage::Format_Invalid && !size.isEmpty()) {
        m_imageFormat = imageFormat;
        QAbstractVideoSurface::start(format);
        return true;
    } else {
        return false;
    }
}

bool MyVideoSurface::framesExists()
{
    if (!m_targetImage.isNull())
        return true;
    else
        return false;
}

void MyVideoSurface::hideViewFinder(bool hide)
{
    m_hideViewFinder = hide;
}

bool MyVideoSurface::present(const QVideoFrame &frame)
{
    m_frame = frame;

    if (surfaceFormat().pixelFormat() != m_frame.pixelFormat() ||
            surfaceFormat().frameSize() != m_frame.size()) {
        stop();
        return false;
    } else {
        m_handleFrameCount--;
        if (m_handleFrameCount>0) {
            return true;
        }
        m_handleFrameCount = MAX_FRAME;

        if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {

            // Make QImage from frame
            QImage* newImage = new QImage(m_frame.bits(),
                                          m_frame.width(),
                                          m_frame.height(),
                                          m_frame.bytesPerLine(),
                                          m_imageFormat);

            m_frame.unmap();

            // TODO: Need this really in Symbian^3 !?
            QImage image = newImage->scaled(QSize(newImage->width()*0.7,newImage->height()*0.7));

            if(!isSameImage(&image)) {
                // LED lights
                if (!m_differentFrame) {
                    // Handle first LED light frame only
                    // and then wait darkness
                    m_target->differenceImage();
                    if (!m_ledLights) {
                        // Store time when LED light first
                        m_lastTime = QTime::currentTime();
                        m_ledLights = true;
                    } else {
                        // Calculate to when LED lights second time
                        QTime time = QTime::currentTime();
                        int ms = m_lastTime.msecsTo(time);
                        float kwh = calculate(ms);
                        QString kwhString = QString::number(kwh);
                        kwhString = kwhString.left(5);
                        m_target->kwh(kwhString);
                        m_ledLights = false;
                    }
                }
                m_differentFrame = true;
            } else {
                // No LED
                m_differentFrame = false;
            }

            delete newImage;

            // Update widget
            if (!m_hideViewFinder) {
                m_target->updateVideo();
            }
        }
        return true;
    }
    return true;
}

float MyVideoSurface::calculate(int ms)
{
    float seconds = (float)ms / 1000.0f;
    float pulsesInHour = seconds / 3600.0f;
    float kwh = (pulsesInHour * 3600.0f) / 3600000.0f;
    return kwh;
}

bool MyVideoSurface::isSameImage(QImage* newImage)
{
    if (m_targetImage.isNull()) {
        m_targetImage = newImage->copy();
        return true;
    }

    int differenceCount = 0;
    int height = newImage->height();
    int width = newImage->width();

    for(int row=0;row<height;row++) {
        QRgb* rgb = (QRgb*)newImage->scanLine(row);
        QRgb* rgb2 = (QRgb*)m_targetImage.scanLine(row);
        for(int col=0;col<width;col++) {
            int valueNewImage = qGreen(rgb[col]);
            int valuePreviousImage = qGreen(rgb2[col]);
            int diff = qAbs(valueNewImage-valuePreviousImage);
            if (diff > 40) {
                differenceCount++;
            }
            if (differenceCount > (width*height*0.03)) {
                m_targetImage = newImage->copy();
                return false;
            }
        }
    }

    m_targetImage = newImage->copy();
    return true;
}

bool MyVideoSurface::isFormatSupported(
        const QVideoSurfaceFormat &format, QVideoSurfaceFormat *similar) const
{
    Q_UNUSED(similar);
    const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
    const QSize size = format.frameSize();
    return imageFormat != QImage::Format_Invalid
            && !size.isEmpty()
            && format.handleType() == QAbstractVideoBuffer::NoHandle;
}

QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(
        QAbstractVideoBuffer::HandleType handleType) const
{
    if (handleType == QAbstractVideoBuffer::NoHandle) {
        return QList<QVideoFrame::PixelFormat>()
                << QVideoFrame::Format_RGB32;
    } else {
        return QList<QVideoFrame::PixelFormat>();
    }
}




CameraItem::CameraItem(QDeclarativeItem *parent) :
    QDeclarativeItem(parent)
{
    // Important, otherwise the paint method is never called
    setFlag(QGraphicsItem::ItemHasNoContents, false);

    m_camera = 0;
    m_myVideoSurface = 0;

    // Camera device list
    m_devices = QCamera::availableDevices();
    m_deviceId = 0;  // Set default camera
}

CameraItem::~CameraItem()
{
    if (m_myVideoSurface) {
        m_myVideoSurface->stop();
    }
    if (m_camera) {
        m_camera->stop();
    }
    delete m_camera;
}

void CameraItem::enableCamera(QVariant enable)
{
    bool b = enable.toBool();
    if (b) {
        // Enable camera device
        QTimer::singleShot(10000,this,SLOT(startCamera()));
    } else {
        // Stop camera device
        stopCamera();
    }
}

void CameraItem::stopCamera()
{
    // Stop camera device
    m_showViewFinder = false;
    if (m_myVideoSurface) {
        m_myVideoSurface->stop();
        m_myVideoSurface = 0;
    }
    if (m_camera) {
        m_camera->stop();
    }
    delete m_camera;
    m_camera = 0;
    m_deviceId = 1; // Set default camera
}

void CameraItem::differenceImage()
{
    emit ledOn();
}

void CameraItem::sameImage()
{
    emit ledOff();
}

void CameraItem::updateVideo()
{
    if (m_showViewFinder) {
        update();
    }
}

void CameraItem::kwh(QString value)
{
    emit kwhWaste(value);
}

void CameraItem::hideViewFinder()
{
    m_myVideoSurface->hideViewFinder(true);
}

void CameraItem::showViewFinder()
{
    m_myVideoSurface->hideViewFinder(false);
}

void CameraItem::startCamera()
{
    if (m_camera || m_myVideoSurface) {
        stopCamera();
        enableCamera(QVariant(true));
        return;
    }

    if (m_deviceId > m_devices.count()-1) {
        m_deviceId = 0;
    }
    m_camera = new QCamera(m_devices[m_deviceId]);
    //connect(m_camera, SIGNAL(error(QCamera::Error)), this, SLOT(error(QCamera::Error)));

    // Own video output drawing
    QMediaService* ms = m_camera->service();
    QVideoRendererControl* vrc = ms->requestControl<QVideoRendererControl*>();
    m_myVideoSurface = new MyVideoSurface(this,this,this);
    vrc->setSurface(m_myVideoSurface);

    // Start camera
    m_camera->start();
    m_showViewFinder = true;
}

void CameraItem::error(QCamera::Error e)
{
    //qDebug() << "QCamera::Error: " << e << " " << m_camera->errorString();
}

void CameraItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setRenderHint(QPainter::Antialiasing, false);
    if (m_myVideoSurface && m_myVideoSurface->framesExists()) {
        // Show view finder
        painter->drawImage((this->width()-m_myVideoSurface->m_targetImage.width())*0.5,
                           (this->height()-m_myVideoSurface->m_targetImage.height())*0.5,
                           m_myVideoSurface->m_targetImage);
    } else {
        painter->fillRect(boundingRect(), Qt::transparent);
    }

}
